import os
import subprocess
import logging
import re
from typing import Dict, List, Optional, Tuple

class DSYMManager:
    """dSYM文件管理器，用于管理和验证dSYM文件"""
    
    def __init__(self):
        """初始化dSYM管理器"""
        self.logger = logging.getLogger("DSYMManager")
        self.dsym_cache = {}  # 缓存已验证的dSYM文件信息
    
    def find_dsym_files(self, directory: str) -> Dict[str, str]:
        """
        在指定目录中查找所有dSYM文件
        
        Args:
            directory: 目录路径
            
        Returns:
            模块名到dSYM文件路径的字典
        """
        dsym_files = {}
        
        if not os.path.exists(directory):
            self.logger.error(f"目录不存在: {directory}")
            return dsym_files
        
        # 如果是单个dSYM文件
        if directory.endswith('.dSYM') and os.path.isdir(directory):
            self._process_dsym_file(directory, dsym_files)
            return dsym_files
        
        # 遍历目录查找所有dSYM文件
        for root, dirs, _ in os.walk(directory):
            for dir_name in dirs:
                if dir_name.endswith('.dSYM'):
                    dsym_path = os.path.join(root, dir_name)
                    self._process_dsym_file(dsym_path, dsym_files)
        
        self.logger.info(f"在目录 {directory} 中找到 {len(dsym_files)} 个dSYM文件")
        return dsym_files
    
    def _process_dsym_file(self, dsym_path: str, dsym_files: Dict[str, str]):
        """
        处理单个dSYM文件，提取DWARF文件路径和模块信息
        
        Args:
            dsym_path: dSYM文件路径
            dsym_files: 存储结果的字典
        """
        try:
            # 提取模块名
            module_name = os.path.basename(dsym_path).replace('.dSYM', '')
            
            # 查找DWARF文件
            dwarf_dir = os.path.join(dsym_path, 'Contents', 'Resources', 'DWARF')
            if os.path.exists(dwarf_dir):
                # 检查是否有与模块名匹配的DWARF文件
                dwarf_path = os.path.join(dwarf_dir, module_name)
                if os.path.exists(dwarf_path):
                    dsym_files[module_name] = dwarf_path
                    self.logger.debug(f"找到dSYM文件: {module_name} -> {dwarf_path}")
                else:
                    # 如果没有找到与模块名匹配的DWARF文件，使用目录中的第一个文件
                    dwarf_files = os.listdir(dwarf_dir)
                    if dwarf_files:
                        real_module_name = dwarf_files[0]
                        dwarf_path = os.path.join(dwarf_dir, real_module_name)
                        dsym_files[real_module_name] = dwarf_path
                        # 同时添加不带扩展名的模块名，方便匹配
                        base_name = os.path.splitext(real_module_name)[0]
                        dsym_files[base_name] = dwarf_path
                        self.logger.debug(f"找到dSYM文件: {real_module_name}/{base_name} -> {dwarf_path}")
        except Exception as e:
            self.logger.error(f"处理dSYM文件时出错: {dsym_path}, 错误: {str(e)}")
    
    def verify_dsym(self, dsym_path: str, arch: str = "arm64") -> bool:
        """
        验证dSYM文件是否有效
        
        Args:
            dsym_path: dSYM文件路径
            arch: 目标架构
            
        Returns:
            是否有效
        """
        # 检查文件是否存在
        if not os.path.exists(dsym_path):
            self.logger.error(f"dSYM文件不存在: {dsym_path}")
            return False
        
        try:
            # 使用dwarfdump验证UUID
            cmd = ['dwarfdump', '--uuid', dsym_path]
            result = subprocess.run(cmd, capture_output=True, text=True)
            
            if result.returncode != 0:
                self.logger.error(f"验证dSYM文件失败: {dsym_path}, 错误: {result.stderr}")
                return False
            
            # 检查是否包含指定架构
            uuid_output = result.stdout
            arch_pattern = re.compile(rf'\({arch}\)')
            if not arch_pattern.search(uuid_output):
                self.logger.warning(f"dSYM文件不包含架构 {arch}: {dsym_path}")
                return False
            
            return True
        except Exception as e:
            self.logger.error(f"验证dSYM文件时出错: {dsym_path}, 错误: {str(e)}")
            return False
    
    def get_dsym_uuid(self, dsym_path: str) -> Optional[str]:
        """
        获取dSYM文件的UUID
        
        Args:
            dsym_path: dSYM文件路径
            
        Returns:
            UUID字符串，失败则返回None
        """
        try:
            cmd = ['dwarfdump', '--uuid', dsym_path]
            result = subprocess.run(cmd, capture_output=True, text=True)
            
            if result.returncode != 0:
                return None
            
            # 解析UUID
            uuid_pattern = re.compile(r'UUID: ([0-9A-F-]+)')
            match = uuid_pattern.search(result.stdout)
            if match:
                return match.group(1)
            
            return None
        except Exception:
            return None
    
    def find_matching_dsym(self, module_name: str, dsym_files: Dict[str, str]) -> Optional[str]:
        """
        查找与模块名匹配的dSYM文件
        
        Args:
            module_name: 模块名称
            dsym_files: 模块名到dSYM文件路径的字典
            
        Returns:
            匹配的dSYM文件路径，失败则返回None
        """
        # 直接匹配
        if module_name in dsym_files:
            return dsym_files[module_name]
        
        # 不区分大小写匹配
        module_name_lower = module_name.lower()
        for name, path in dsym_files.items():
            if name.lower() == module_name_lower:
                return path
        
        # 部分匹配
        for name, path in dsym_files.items():
            if module_name_lower in name.lower() or name.lower() in module_name_lower:
                return path
        
        return None


# 使用示例
if __name__ == "__main__":
    # 配置日志
    logging.basicConfig(level=logging.INFO)
    
    # 示例代码
    manager = DSYMManager()
    dsym_dir = "/Users/user/Desktop/dSYMs"
    
    # 查找所有dSYM文件
    dsym_files = manager.find_dsym_files(dsym_dir)
    
    # 打印找到的dSYM文件
    for module, path in dsym_files.items():
        print(f"模块: {module}, 路径: {path}")
        
        # 验证dSYM文件
        is_valid = manager.verify_dsym(path)
        print(f"有效性: {is_valid}")
        
        # 获取UUID
        uuid = manager.get_dsym_uuid(path)
        print(f"UUID: {uuid}")
        print("-" * 50) 